# frozen_string_literal: true

class Auth::DiscordAuthenticator < Auth::ManagedAuthenticator
  class DiscordStrategy < OmniAuth::Strategies::OAuth2
    option :name, "discord"
    option :scope, "identify email guilds"

    option :client_options,
           site: "https://discord.com/api",
           authorize_url: "oauth2/authorize",
           token_url: "oauth2/token"

    option :authorize_options, %i[scope permissions]

    uid { raw_info["id"] }

    info do
      {
        name: raw_info["global_name"] || raw_info["username"],
        nickname: raw_info["username"],
        email: raw_info["verified"] ? raw_info["email"] : nil,
        image: "https://cdn.discordapp.com/avatars/#{raw_info["id"]}/#{raw_info["avatar"]}",
      }
    end

    extra { { "raw_info" => raw_info } }

    def raw_info
      @raw_info ||=
        access_token
          .get("users/@me")
          .parsed
          .merge(guilds: access_token.get("users/@me/guilds").parsed)
    end

    def callback_url
      full_host + script_name + callback_path
    end
  end

  def name
    "discord"
  end

  def display_name
    "Discord"
  end

  def provider_url
    "https://discord.com"
  end

  def enabled?
    SiteSetting.enable_discord_logins?
  end

  def register_middleware(omniauth)
    omniauth.provider DiscordStrategy,
                      setup:
                        lambda { |env|
                          strategy = env["omniauth.strategy"]
                          strategy.options[:client_id] = SiteSetting.discord_client_id
                          strategy.options[:client_secret] = SiteSetting.discord_secret
                        }
  end

  def after_authenticate(auth_token, existing_account: nil)
    allowed_guild_ids = SiteSetting.discord_trusted_guilds.split("|")

    if allowed_guild_ids.length > 0
      user_guild_ids = auth_token.extra[:raw_info][:guilds].map { |g| g["id"] }
      if (user_guild_ids & allowed_guild_ids).empty? # User is not in any allowed guilds
        return(
          Auth::Result.new.tap do |auth_result|
            auth_result.failed = true
            auth_result.failed_reason = I18n.t("discord.not_in_allowed_guild")
          end
        )
      end
    end

    super
  end

  # the `info` block above only picks the email from Discord API if it's verified
  def primary_email_verified?(auth_token)
    true
  end
end
